package cn.ciis.basic.kit;

import com.jfinal.kit.StrKit;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/** ImageKit 图片高保真缩放与裁剪，不依赖于任何第三方库 */
public class ImageKit {

  private static final String[] IMG_EXTS = new String[] {"jpg", "jpeg", "png", "bmp"};

  public static String getExtName(String fileName) {
    int index = fileName.lastIndexOf('.');
    if (index != -1 && (index + 1) < fileName.length()) {
      return fileName.substring(index + 1);
    } else {
      return null;
    }
  }

  /** 通过文件扩展名，判断是否为支持的图像文件，支持则返回 true，否则返回 false */
  public static boolean isImageExtName(String fileName) {
    if (StrKit.isBlank(fileName)) {
      return false;
    }
    fileName = fileName.trim().toLowerCase();
    String ext = getExtName(fileName);
    if (ext != null) {
      for (String s : IMG_EXTS) {
        if (s.equals(ext)) {
          return true;
        }
      }
    }
    return false;
  }

  public static final boolean notImageExtName(String fileName) {
    return !isImageExtName(fileName);
  }

  /**
   * 以 maxWidth 为界对图像进行缩放，高保真保存 1：当图像 width > maxWidth 时，将宽度变为 maxWidth，高度等比例进行缩放，高保真保存 2:当图像 width
   * <= maxWidth 时，宽高保持不变，只进行高保真保存
   */
  public static void zoom(int maxWidth, File srcFile, String saveFile) {
    float quality = 0.8f;

    try {
      BufferedImage srcImage = ImageIO.read(srcFile);
      int srcWidth = srcImage.getWidth();
      int srcHeight = srcImage.getHeight();

      // 当宽度在 maxWidth 范围之内，不改变图像宽高，只进行图像高保真保存
      if (srcWidth <= maxWidth) {
        /**
         * 如果图像不进行缩放的话， resize 就没有必要了， 经过测试是否有 resize 这一步，生成的结果图像完全一样，一个字节都不差 所以删掉
         * resize，可以提升性能，少一步操作
         */
        // BufferedImage ret = resize(srcImage, srcWidth, srcHeight);
        // saveWithQuality(ret, quality, saveFile);
        saveWithQuality(srcImage, quality, saveFile);
      }
      // 当宽度超出 maxWidth 范围，将宽度变为 maxWidth，而高度按比例变化
      else {
        float scalingRatio = (float) maxWidth / (float) srcWidth; // 计算缩放比率
        float maxHeight = ((float) srcHeight * scalingRatio); // 计算缩放后的高度
        BufferedImage ret = resize(srcImage, maxWidth, (int) maxHeight);
        saveWithQuality(ret, quality, saveFile);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 对图片进行剪裁，只保存选中的区域
   *
   * @param sourceImageFile 原图
   * @param left
   * @param top
   * @param width
   * @param height
   */
  public static BufferedImage crop(
      String sourceImageFile, int left, int top, int width, int height) {
    if (notImageExtName(sourceImageFile)) {
      throw new IllegalArgumentException("只支持如下几种类型的图像文件：jpg、jpeg、png、bmp");
    }

    try {
      BufferedImage bi = ImageIO.read(new File(sourceImageFile));
      width = Math.min(width, bi.getWidth());
      height = Math.min(height, bi.getHeight());
      if (width <= 0) {
        width = bi.getWidth();
      }
      if (height <= 0) {
        height = bi.getHeight();
      }

      left = Math.min(Math.max(0, left), bi.getWidth() - width);
      top = Math.min(Math.max(0, top), bi.getHeight() - height);

      BufferedImage subImage = bi.getSubimage(left, top, width, height);
      BufferedImage subImage1 = subImage;
      return subImage1;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static void save(BufferedImage bi, String outputImageFile) {
    FileOutputStream newImage = null;
    try {
      ImageIO.write(bi, getExtName(outputImageFile), new File(outputImageFile));
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      if (newImage != null) {
        try {
          newImage.close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }
  }

  /** 高保真缩放 */
  public static BufferedImage resize(BufferedImage bi, int toWidth, int toHeight) {
    Graphics g = null;
    try {
      // 从 BufferedImage 对象中获取一个经过缩放的 image
      Image scaledImage = bi.getScaledInstance(toWidth, toHeight, Image.SCALE_SMOOTH);
      // 创建 BufferedImage 对象，存放缩放过的 image
      BufferedImage ret = new BufferedImage(toWidth, toHeight, BufferedImage.TYPE_INT_RGB);
      g = ret.getGraphics();
      g.drawImage(scaledImage, 0, 0, null);
      return ret;
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      if (g != null) {
        g.dispose();
      }
    }
  }


  public static void saveWithQuality(BufferedImage im, float quality, String outputImageFile) {
    ImageWriter writer = null;
    FileOutputStream newImage = null;
    try {
      BufferedImage newBufferedImage =
          new BufferedImage(im.getWidth(), im.getHeight(), BufferedImage.TYPE_INT_RGB);
      newBufferedImage.createGraphics().drawImage(im, 0, 0, Color.WHITE, null);
      // 输出到文件流
      newImage = new FileOutputStream(outputImageFile);

      writer = ImageIO.getImageWritersBySuffix("jpg").next();
      ImageWriteParam param = writer.getDefaultWriteParam();
      param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
      param.setCompressionQuality(quality);
      ImageOutputStream os = ImageIO.createImageOutputStream(newImage);
      writer.setOutput(os);
      writer.write(null, new IIOImage(newBufferedImage, null, null), param);
      os.flush();
      os.close();
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if (writer != null) {
        try {
          writer.dispose();
        } catch (Throwable e) {
        }
      }
      if (newImage != null) {
        try {
          newImage.close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }
  }

}
